package org.budo.support.application.registry.util;

import java.util.List;

import org.budo.support.application.registry.BudoApplication;
import org.budo.support.application.registry.BudoApplicationRegistry;
import org.budo.support.application.registry.NoApplicationAvailableException;
import org.budo.support.application.registry.NoApplicationMatchedException;
import org.budo.support.http.client.HttpUtil;
import org.budo.support.lang.util.NumberUtil;
import org.budo.support.lang.util.StringUtil;
import org.budo.support.spring.util.SpringUtil;

import lombok.extern.slf4j.Slf4j;

/**
 * @author lmw
 */
@Slf4j
public class BudoApplicationRegistryUtil {
    public static final String NO_APPLICATION_AVAILABLE = "BUDO_NO_APPLICATION_AVAILABLE";

    /**
     * @param applicationName
     */
    public static BudoApplication lookupBudoApplication(String applicationName) {
        BudoApplicationRegistry applicationRegistry = SpringUtil.getBean(BudoApplicationRegistry.class);
        if (null == applicationRegistry) {
            throw new RuntimeException("#14 applicationRegistry=" + applicationRegistry);
        }

        List<BudoApplication> lookup = applicationRegistry.lookup(applicationName);
        if (null == lookup || lookup.isEmpty()) {
            throw new NoApplicationMatchedException("#21 applicationName=" + applicationName + ", lookup=" + lookup + ", applicationRegistry=" + applicationRegistry, applicationName);
        }

        // 按启动时间倒序排序
        lookup.sort((a, b) -> b.getStartAt().compareTo(a.getStartAt())); // b compareTo a // 大的优先 后启动的优先

        // 性能问题 外部做缓存
        for (BudoApplication application : lookup) {
            String healthz = "http://" + application.getIp() + ":" + application.getPort() + application.getContextPath() + "/budo-graph/healthz";
            if (HttpUtil.ok_200(healthz, 500, 1000)) {
                return application;
            }

            log.warn("#47 not ok, applicationName=" + applicationName + ", healthz=" + healthz + ", application=" + application);
        }

        throw new NoApplicationAvailableException("#21 applicationName=" + applicationName + ", lookup=" + lookup + ", applicationRegistry=" + applicationRegistry, applicationName);
    }

    public static BudoApplication findBudoApplication(String ip, Integer port) {
        BudoApplicationRegistry budoApplicationRegistry = SpringUtil.getBean(BudoApplicationRegistry.class);
        if (null == budoApplicationRegistry) {
            log.warn("#223 BudoApplicationRegistry not fount");
            return null;
        }

        List<BudoApplication> lookup = budoApplicationRegistry.lookup(BudoApplicationRegistry.APPLICATION_NAME_ANY);
        if (null == lookup || lookup.isEmpty()) {
            log.warn("#280 lookup = " + lookup);
            return null;
        }

        for (BudoApplication application : lookup) {
            if (StringUtil.equals(ip, application.getIp()) && NumberUtil.equals(port, application.getPort())) {
                return application;
            }
        }

        return null;
    }

    public static Boolean isNoApplicationMatched(Throwable e) {
        if (null == e) {
            return false;
        }

        if (e instanceof NoApplicationMatchedException) {
            return true;
        }

        return isNoApplicationMatched(e.getCause());
    }

    public static Boolean isNoApplicationAvailable(Throwable e) {
        if (null == e) {
            return false;
        }

        if (e instanceof NoApplicationAvailableException) {
            return true;
        }

        return isNoApplicationMatched(e.getCause());
    }

    public static String getApplicationName(Throwable e) {
        if (null == e) {
            return null;
        }

        if (e instanceof NoApplicationMatchedException) {
            return ((NoApplicationMatchedException) e).getApplicationName();
        }

        if (e instanceof NoApplicationAvailableException) {
            return ((NoApplicationAvailableException) e).getApplicationName();
        }

        // 递归 下级异常
        Throwable cause = e.getCause();
        return getApplicationName(cause);
    }
}